package gohttp

import (
	"dgo/goutils/utils"
	"net/http"
	"strings"
	"sync"
)

type GoHttpServeMux struct {
	handleMap          utils.SyncMap
	prefixPattern      string
	isRootPattern      bool
	mu                 sync.RWMutex
	es                 []muxEntry // slice of entries sorted from longest to shortest.
	OnHttpRequestEntry func(w http.ResponseWriter, r *http.Request) bool
}

var (
	DefaultHttpServeMux *GoHttpServeMux
)

func CheckDefaultHttpServeMux(prefixPattern string) *GoHttpServeMux {
	if DefaultHttpServeMux == nil {
		DefaultHttpServeMux = NewGoHttpServeMux(prefixPattern)
	}
	return DefaultHttpServeMux
}

func NewGoHttpServeMux(prefixPattern string) *GoHttpServeMux {
	prefixPattern = strings.TrimSuffix(prefixPattern, "/")
	rval := &GoHttpServeMux{}
	rval.prefixPattern = prefixPattern
	rval.isRootPattern = len(prefixPattern) == 0
	if rval.isRootPattern {
		http.HandleFunc("/", rval.rootHttpHandleMap)
	} else {
		http.HandleFunc(rval.prefixPattern+"/", rval.patternHttpHandleMap)
	}
	return rval
}

func (this *GoHttpServeMux) RegisterHandleFunc(pattern string, fn http.HandlerFunc) bool {
	return this.RegisterHandler(pattern, fn)
}

func (this *GoHttpServeMux) RegisterHandler(pattern string, handler http.Handler) bool {
	_, loaded := this.handleMap.LoadOrStore(pattern, handler)
	if !loaded {
		if pattern[len(pattern)-1] == '/' {
			this.mu.Lock()
			defer this.mu.Unlock()
			e := muxEntry{h: handler, pattern: pattern}
			this.es = appendSorted(this.es, e)
		}
	}
	return !loaded
}

func (this *GoHttpServeMux) UnRegisterHandleFunc(pattern string) {
	if this.handleMap.Remove(pattern) {
		if pattern[len(pattern)-1] == '/' {
			this.mu.Lock()
			defer this.mu.Unlock()
			this.es = deleteSorted(this.es, pattern)
		}
	}
}

func (this *GoHttpServeMux) match(path string) (h http.Handler, pattern string) {
	rec, _ := this.handleMap.Load(path)
	if rec != nil {
		return rec.(http.Handler), path
	}

	this.mu.RLock()
	defer this.mu.RUnlock()
	for _, e := range this.es {
		if strings.HasPrefix(path, e.pattern) {
			return e.h, e.pattern
		}
	}

	return nil, ""
}

func (this *GoHttpServeMux) innerHttpHandleMap(pattern string, w http.ResponseWriter, r *http.Request) {
	if utils.GoFunCatchException {
		defer utils.PanicHandlerWithDebugString(pattern)
	}
	evt := this.OnHttpRequestEntry
	if evt != nil {
		if !evt(w, r) {
			return
		}
	}

	rec, _ := this.match(pattern)
	if rec == nil {
		http.Error(w, "404 page not found", 404)
		return
	}
	h := rec.(http.Handler)
	h.ServeHTTP(w, r)
}

func (this *GoHttpServeMux) patternHttpHandleMap(w http.ResponseWriter, r *http.Request) {
	url := r.URL.Path
	url = strings.TrimPrefix(url, this.prefixPattern)
	r.URL.Path = url
	this.innerHttpHandleMap(url, w, r)
}

func (this *GoHttpServeMux) rootHttpHandleMap(w http.ResponseWriter, r *http.Request) {
	this.innerHttpHandleMap(r.URL.Path, w, r)
}

/*
**

	注册
*/
func HandleFunc(pattern string, fn http.HandlerFunc) bool {
	if DefaultHttpServeMux == nil {
		return false
		//panic("DefaultHttpHandleMap is nil, Http serivce is shutdown! invoke CheckDefaultHttpServeMux()")
	}
	return DefaultHttpServeMux.RegisterHandleFunc(pattern, fn)
}

/*
**

	为了匹配函数类型
*/
func HttpHandle(pattern string, handler http.Handler) {
	Handle(pattern, handler)
}

/*
**

	注册
*/
func Handle(pattern string, handler http.Handler) bool {
	if DefaultHttpServeMux == nil {
		return false
		//panic("DefaultHttpHandleMap is nil")
	}
	return DefaultHttpServeMux.RegisterHandler(pattern, handler)
}

func UnHandle(pattern string) {
	if DefaultHttpServeMux != nil {
		DefaultHttpServeMux.UnRegisterHandleFunc(pattern)
	}
}
